package concurrecy.lock2;


import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

class JannaReentrantLock implements Lock {
    // 锁状态：0 未加锁，其他 加锁
    private AtomicInteger state = new AtomicInteger(0);
    // 锁的拥有者
    private Thread owner;
    // 等待锁的线程
    private LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<>();

    @Override
    public boolean tryLock() {
        // cas从0修改为1，则加锁成功
        if(state.compareAndSet(0, 1)) {
            // 把锁的拥有者修改为当前线程
            owner = Thread.currentThread();
            return true;
        } else {
            // 如果当前线程是锁的拥有者，计数加1
            if(owner == Thread.currentThread()) {
                state.incrementAndGet();
                return true;
            }
        }
        return false;
    }

    @Override
    public void lock() {
        // 如果加锁失败
        if(!tryLock()) {
            Thread current = Thread.currentThread();
            // 加入等待队列
            waiters.add(current);
            for(;;) {
//                // 获取队列头部元素
//                Thread first = waiters.peek();
//                // 如果是当前线程，则尝试获取锁
//                if(first == Thread.currentThread()) {
//                    // 没有获取到，则阻塞
//                    if(!tryLock())
//                        LockSupport.park();
//                    else {
//                        // 获取到锁，从队列中移除
//                        waiters.remove(first);
//                        return;
//                    }
//                } else {
//                    // 不是当前线程，阻塞
//                    LockSupport.park();
//                }
                // 抢到了锁
                if(tryLock()) {
                    // 从队列中移除
                    waiters.remove(current);
                    return;
                }
                // 没有抢到锁，阻塞
                LockSupport.park();
            }
        }
    }

    public boolean tryUnlock() {
        if(owner != Thread.currentThread())
            throw new IllegalMonitorStateException();

        int t = state.get();
        // 如果释放后，状态为0，说明已经释放完了
        if(t - 1 == 0) {
            owner =  null;
            state.set(t - 1);
            return true;
        } else
            state.set(t - 1);
        return false;
    }

    @Override
    public void unlock() {
        // 释放成功
        if(tryUnlock()) {
            Thread thread = waiters.peek();
            if(thread != null)
                // 唤醒等待队列中的第一个线程
                LockSupport.unpark(thread);
        }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}

class LockChecker implements Runnable {

    private Lock lock;

    public LockChecker(Lock lock) {
        this.lock = lock;
    }

    @Override
    public String toString() {
        return Thread.currentThread().getName();
    }

    @Override
    public void run() {
        System.out.println(this + " 开始抢锁");
        lock.lock();
        System.out.println(this + " 抢到锁");
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            System.out.println(this + " sleep interrupted");
        } finally {
            lock.unlock();
            System.out.println(this + " 释放锁");
        }
    }
}

public class MyReentrantLockDemo {
    /**
     * 测试可重入
     * @throws InterruptedException
     */
    static void test1(Lock lock) throws InterruptedException {
        lock.lock();
        System.out.println("第一次加锁成功");

        new Thread(() -> {
            System.out.println("thread 准备获取锁 ...");
            lock.lock();
            System.out.println("thread 加锁成功");
            lock.unlock();
        }).start();

        lock.lock();
        System.out.println("第二次加锁成功");
        Thread.sleep(100);
        lock.unlock();
        System.out.println("释放锁");
        Thread.sleep(100);
        lock.unlock();
        System.out.println("释放锁");
    }

    /**
     * 测试多线程获取锁
     * @param lock
     */
    static void test2(Lock lock) {
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(new LockChecker(lock));
        service.execute(new LockChecker(lock));
        service.execute(new LockChecker(lock));
        service.execute(new LockChecker(lock));
        service.shutdown();
    }

    /**
     * 测试多线程竞争
     */
    static void test3(Lock lock, int n) throws InterruptedException {
        class Data {
            int x = 0;
        }
        final Data data = new Data();
        ExecutorService executorService = Executors.newCachedThreadPool();
        CountDownLatch latch = new CountDownLatch(n);
        long point = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            executorService.execute(() -> {
                for (int j = 0; j < 100_000; j++) {
                    lock.lock();
                    try {
                        data.x ++;
                        lock.lock();
                        try {
                            data.x ++;
                        } finally {
                            lock.unlock();
                        }
                    } finally {
                        lock.unlock();
                    }
                }
                latch.countDown();
            });
        }
        latch.await();
        System.out.println("use " + (System.currentTimeMillis() - point));
        executorService.shutdown();
        System.out.println(data.x);
    }

    public static void main(String[] args) throws InterruptedException {
//        Lock lock = new KaneReentrantLock_Demo();
//        Lock lock = new ReentrantLock();
        Lock lock = new JannaReentrantLock();
//        test1(lock);
//        test2(lock);
        test3(lock, 8);
    }
}
